home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
crypt
/
tarchiv
/
aspi.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-01-27
|
20KB
|
765 lines
{ ASPI.TPU }
{ Andreas Schiffler, U of S, 1994 }
{ ASPI to SCSI-Tape (i.e. Exabyte) Interface Unit to provide most of the }
{ functions available for the EXB-8500 tape drive. }
{ 286/287 instructions enabled for assembler code }
{$G+}
Unit ASPI;
Interface
Uses Dos, Crt;
Const
{ Communication with ASPI over a data-block (SRB): }
{ |---------64-------------|------6/10-------|--------- N ----------| }
{ ASPI-command SCSI-Command from Device (sense)
{ Field lengths }
ASPIBlockLength = 64;
CommandBlockLength = 6;
SenseBlockLength = 29;
SRBBlockLength = ASPIBlockLength+CommandBlockLength+SenseBlockLength+4;
{ Field locations }
CommandLocation = ASPIBlockLength;
SenseLocation = ASPIBlockLength+CommandBlockLength;
{ Local data buffer }
DataBufferSize = 256;
Type
PDataBuffer = ^TDataBuffer;
TDataBuffer = Array [0..(DataBufferSize-1)] of Byte;
{ These values are defined after a call to INQUIRY. }
{ }
TapeInfo = Record
Valid : Boolean;
HostNumber : Byte;
Target : Byte;
Host : String[32];
Device : String[16];
Vendor : String[8];
Product : String[16];
Revision : String[4];
Serial : String[10];
End;
{ ERROR indicates any kind of status which requires attention. }
{ WRITE_PROTECTION_ON is only defined when ERROR is false.}
{ }
TapeStatus = Record
Error : Boolean;
ASPI : String[60];
Host : String[60];
Target : String[60];
Sense : String[60];
SenseExt : String[60];
FilemarkDetected : Boolean;
TapeNotPresent : Boolean;
BeginningOfTape : Boolean;
EndOfTape : Boolean;
WriteProtectOn : Boolean;
End;
SRBType = Array [0..(SRBBlockLength-1)] Of Byte;
PASPITape = ^ASPITape;
ASPITape = Object
SRB : SRBType;
HSeg_Data,
LSeg_Data,
HOfs_Data,
LOfs_Data : Byte;
SenseStart : Byte;
DataBuffer : PDataBuffer;
TarBlock : Byte;
Info : TapeInfo;
Status : TapeStatus;
Constructor Init (TargetNumber : Byte);
Destructor Done;
Procedure CallASPI;
Procedure Inquiry;
Procedure RequestSense;
Procedure TestUnitReady;
Procedure ParseStatus;
Procedure ModeSelect(BufferSize : Word);
Procedure ReadData(Buffer : Pointer; BufferSize : Word);
Procedure WriteData(Buffer : Pointer; BufferSize : Word);
Function TapePosition : Longint;
Procedure LocateTape (Location : Longint);
Procedure ASPIReset;
Procedure WriteFilemark (Number : Byte);
Procedure SpaceFilemark (Spacing : Longint);
Procedure GotoEnd;
Procedure Rewind;
Procedure Unload;
Procedure Load;
Procedure Erase;
End;
Implementation
Var
GlobalSRB : SRBType; { THE ASPI Command Block }
LockSRB : Boolean; { Sync. flag for multiple objects }
{ Utility routines }
{ ================ }
{ ASPITape routines }
{ ================= }
Constructor ASPITape.Init (TargetNumber : Byte);
Begin
{ Initialize variables }
Info.Valid := False;
Info.Target := TargetNumber;
Info.HostNumber := 0;
Info.Host := '?';
Info.Vendor := '?';
Info.Product := '?';
Info.Revision := '?';
Info.Serial := '?';
Info.Device := '?';
Status.Error := False;
Status.ASPI := '?';
Status.Host := '?';
Status.Target := '?';
Status.Sense := '?';
Status.SenseExt := '?';
Status.FilemarkDetected := False;
Status.TapeNotPresent := False;
Status.BeginningOfTape := False;
Status.EndOfTape := False;
Status.WriteProtectOn := False;
New(DataBuffer); { Create and Init data buffer }
LSeg_Data := Lo(Seg(DataBuffer^));
HSeg_Data := Hi(Seg(DataBuffer^));
LOfs_Data := Lo(Ofs(DataBuffer^));
HOfs_Data := Hi(Ofs(DataBuffer^));
FillChar (DataBuffer^[0],DataBufferSize,0);
End;
Destructor ASPITape.Done;
Begin
Dispose (DataBuffer);
End;
Procedure ASPITape.CallASPI;
Const
Message : Array [0..9] of Char = ('S','C','S','I','M','G','R','$',#0,#0);
Var
ASPIEntry : Array [0..3] of Byte;
Timeout : Longint;
Manager : Byte;
Begin
{ Set Manager flag }
Manager := 0;
{ Wait until GlobalSRB gets unlocked. }
While LockSRB Do ;
{ Lock SRB transfer to global data and execute. }
LockSRB := True;
Move(SRB,GlobalSRB,SizeOf(SRBType));
Asm
Push DS
mov ax,$3d00
lea dx,Message
int $21
jc @NoManager
push ax
mov bx,ax
mov ax,$4402
lea dx,ASPIEntry
mov cx,4
int $21
mov ah,$3e
pop bx
int $21
push ds
lea bx,[GlobalSRB]
push bx
lea bx,ASPIEntry
call dword ptr [bx]
add sp,4
mov al,1;
mov manager,al
@NoManager:
Pop DS
End;
{ Check if a ASPI Manager was present }
IF (Manager=1) Then Begin
{ Wait for command to finish - timeout after a 4 hours. }
{ This will give time for slow commands liek ERASE to finish. }
Timeout := 0;
Repeat
INC(Timeout);
Delay (10);
Until ((GlobalSRB[1]<>0) OR (Timeout=4*60*60*100));
If Timeout=4*60*60*100 Then GlobalSRB[1]:=$FF; { report Timeout Error }
End Else Begin
GlobalSRB[1]:=$FE; { No ASPI Manager Error }
Info.Valid := False;
End;
{ Transfer to local buffer and unlock. }
Move (GlobalSRB,SRB,SizeOf(SRBType));
LockSRB := False;
{ Check for request sense command and any errors from that }
If (SRB[0]=2) AND (SRB[64]=$03) AND ((DataBuffer^[2] AND $0F)<>0) Then Begin
{ Transfer data to SRB and set flag to check }
SRB[25] := $02;
Move (DataBuffer^,SRB[70],29);
End;
{ Determine Sense Location }
SenseStart := 64+SRB[23];
{ Determine error status from ASPI, Host and Sense area }
Status.Error := (SRB[1]<>1) OR (SRB[24]<>0) OR (SRB[25]<>0) OR ((SRB[SenseStart+2] AND $0F)<>0);
End;
{ Call this whenever the ERROR is set to fill the STATUS fields. }
{ }
Procedure ASPITape.ParseStatus;
Begin
If SRB[1]=$FE Then Begin
Status.ASPI := 'No ASPI-Manager present';
Status.Host := '?';
Status.Target := '?';
Status.Sense := '?';
Status.SenseExt := '?';
Status.FilemarkDetected := False;
Status.TapeNotPresent := False;
Status.BeginningOfTape := False;
Status.WriteProtectOn := False;
Status.EndOfTape := False;
End Else Begin
{ Get all the information }
RequestSense;
{ Determine status }
Case SRB[1] of
0: Status.ASPI := 'SCSI request in progress';
1: Status.ASPI := 'SCSI request completed without error';
2: Status.ASPI := 'SCSI request aborted by host';
4: Status.ASPI := 'SCSI request completed with error';
$80: Status.ASPI := 'SCSI request invalid';
$81: Status.ASPI := 'Invalid host adapter number';
$82: Status.ASPI := 'SCSI adapter not installed';
$FF: Status.ASPI := 'SCSI request in progress, timeout';
End;
Case SRB[24] of
$00: Status.Host := 'Host adapter did not detect any error';
$11: Status.Host := 'Selection timeout';
$12: Status.Host := 'Data overrun/underrun';
$13: Status.Host := 'Unexpected bus free';
$14: Status.Host := 'Target bus phase sequence failure';
End;
Case SRB[25] of
$00: Status.Target := 'No/Good target status';
$02: Status.Target := 'Sense data available';
$04: Status.Target := 'Condition met';
$08: Status.Target := 'Specified target busy';
$14: Status.Target := 'Intermediate condition met';
$18: Status.Target := 'Reservation conflict';
$22: Status.Target := 'Command terminated';
$28: Status.Target := 'Queue full';
End;
If SRB[25]=$02 Then Begin
{ Sense available }
Case (SRB[SenseStart+2] AND $0F) of
0: Begin
Status.Sense := 'No Sense';
Case SRB[82] of
$00: Case SRB[83] of
$01: Status.SenseExt := 'Filemark encountered during read';
$02: Status.SenseExt := 'LEOT encountered';
$04: Status.SenseExt := 'LBOT detected';
End;
$3b: Status.SenseExt := 'Tape position error at beginning';
End;
End;
1: Begin
Status.Sense := 'Recovered Error';
Status.SenseExt := '';
End;
2: Begin
Status.Sense := 'Not Ready';
Case SRB[SenseStart+12] of
$04: Case SRB[SenseStart+13] of
$00: Status.SenseExt := 'Tape not present';
$01: Status.SenseExt := 'Tape loading or rewinding';
$02: Status.SenseExt := 'Tape motion command required';
$03: Status.SenseExt := 'Unrecoverable hardware error';
End;
$3A: Status.SenseExt := 'Tape not present';
End;
End;
3: Begin
Status.Sense := 'Medium Error';
Case SRB[SenseStart+12] of
$3b: Case SRB[SenseStart+13] of
$00: Status.SenseExt := 'Position error';
$02: Status.SenseExt := 'Position error at end';
End;
$00: Status.SenseExt := 'Undetermined error';
$03: Status.SenseExt := 'Excessive write errors';
$09: Status.SenseExt := 'Tracking error';
$0C: Status.SenseExt := 'Hard write error';
$11: Status.SenseExt := 'Hard read error';
End;
End;
4: Begin
Status.Sense := 'Hardware Error';
Case SRB[SenseStart+12] of
$40: Case SRB[83] of
$80: Status.SenseExt := 'DPATH error';
$81: Status.SenseExt := 'DPATH underrun';
End;
$00: Status.SenseExt := 'Undetermined';
$09: Status.SenseExt := 'Track follow error';
$44: Status.SenseExt := 'Internal failure/Software hang';
End;
End;
5: Begin
Status.Sense := 'Illegal Request';
Case SRB[SenseStart+12] of
$1A: Status.SenseExt := 'Parameter list length error';
$20: Status.SenseExt := 'Illegal CDB code';
$24: Status.SenseExt := 'Invalid CDB field';
$25: Status.SenseExt := 'LUN not supported';
$26: Status.SenseExt := 'Invalid parameter';
$3D: Status.SenseExt := 'LUN invalid';
$81: Status.SenseExt := 'Fixed mode mismatch';
End;
End;
6: Begin
Status.Sense := 'Unit Attention';
Case SRB[SenseStart+12] of
$28: Status.SenseExt := 'Media has been changed';
$29: Status.SenseExt := 'Reset has occured';
$2A: Status.SenseExt := 'Mode select has been changed';
$3F: Status.SenseExt := 'New microcode loaded';
End;
End;
7: Begin
Status.Sense := 'Data Protect';
Status.SenseExt := 'The tape is write protected';
End;
8: Begin
Status.Sense := 'Blank Check';
Status.SenseExt := 'End of data encountered on read';
End;
9: Begin
Status.Sense := 'EXABYTE specific error';
Status.SenseExt := '';
End;
10: Begin
Status.Sense := 'Copy Aborted';
Status.SenseExt := '';
End;
11: Begin
Status.Sense := 'Aborted Command';
Case SRB[SenseStart+12] of
$45: Status.SenseExt := 'Reselect failed';
$47: Status.SenseExt := 'SCSI bus failure during write';
$48: Status.SenseExt := 'Initiator detected error';
$49: Status.SenseExt := 'Invalid message recieved';
End;
End;
13: Begin
Status.Sense := 'Volume Overflow';
Status.SenseExt := 'PEOT encountered during write';
End;
End;
{ Set booleans }
Status.FilemarkDetected := ((SRB[SenseStart+ 2] AND $80)<>0);
Status.TapeNotPresent := ((SRB[SenseStart+19] AND $02)<>0);
Status.BeginningOfTape := ((SRB[SenseStart+19] AND $01)<>0);
Status.WriteProtectOn := ((SRB[SenseStart+20] AND $20)<>0);
Status.EndOfTape := ((SRB[SenseStart+21] AND $04)<>0);
End Else Begin
{ Sense not available }
Status.Sense := '';
Status.SenseExt := '';
End;
End;
End;
{ Used by ParseStatus. }
{ }
Procedure ASPITape.RequestSense;
Begin
{ ASPI Setup for sense inquiry }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=29;
SRB[15]:=LOfs_Data;
SRB[16]:=HOfs_Data;
SRB[17]:=LSeg_Data;
SRB[18]:=HSeg_Data;
SRB[23]:=6;
SRB[64]:=$03;
SRB[68]:=29;
{ Execute }
CallASPI;
End;
{ Determine host and tape parameters, thus initializing the INFO field. }
{ }
Procedure ASPITape.Inquiry;
Begin
{ ASPI Setup for host inquiry }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=0;
SRB[ 8]:=Info.Target;
{ Execute }
CallASPI;
If Status.Error Then ParseStatus;
{ Extract information }
Move (SRB[10],Info.Host[1],32);
Info.Host[0]:=Chr(32);
Info.HostNumber := SRB[8];
{ Setup for device inquiry }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=1;
SRB[ 8]:=Info.Target;
{ Execute }
CallASPI;
{ Extract Information }
Info.Valid := False;
If Status.Error Then Begin
ParseStatus;
Info.Device := 'Unknown';
End Else
Case SRB[10] of
0: Begin Info.Device:='Disk'; Info.Valid := True; End;
1: Begin Info.Device:='Tape'; Info.Valid := True; End;
2: Begin Info.Device:='Printer'; Info.Valid := True; End;
3: Begin Info.Device:='Processor'; Info.Valid := True; End;
4: Begin Info.Device:='WORM'; Info.Valid := True; End;
5: Begin Info.Device:='CD-ROM'; Info.Valid := True; End;
6: Begin Info.Device:='Scanner'; Info.Valid := True; End;
7: Begin Info.Device:='Optical Memory'; Info.Valid := True; End;
8: Begin Info.Device:='Medium Changer'; Info.Valid := True; End;
9: Begin Info.Device:='Communication'; Info.Valid := True; End;
Else
Info.Device := 'Unknown';
End;
If Info.Valid Then Begin
{ ASPI Setup for specific inquiry }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=106;
SRB[15]:=LOfs_Data;
SRB[16]:=HOfs_Data;
SRB[17]:=LSeg_Data;
SRB[18]:=HSeg_Data;
SRB[23]:=6;
SRB[64]:=$12;
SRB[68]:=106;
{ Execute }
CallASPI;
{ Extract product information to Pascal strings }
Move (DataBuffer^[8],Info.Vendor[1],8);
Info.Vendor[0]:=#8;
Move (DataBuffer^[16],Info.Product[1],16);
Info.Product[0]:=#16;
Move (DataBuffer^[32],Info.Revision[1],4);
Info.Revision[0]:=#4;
Move (DataBuffer^[96],Info.Serial[1],10);
Info.Serial[0]:=#10;
End;
End;
{ This routine is used to prepare the tape drive for a "good" status.}
{ Keep calling this routine until ERROR is false. }
{ }
Procedure ASPITape.TestUnitReady;
Begin
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=0;
{ Execute }
CallASPI;
End;
{ Set the tape with current 'BlockSize' and use buffered-mode writes. }
{ }
Procedure ASPITape.ModeSelect (BufferSize : Word);
Begin
{ ASPI Setup for Mode Select }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=12;
SRB[15]:=LOfs_Data;
SRB[16]:=HOfs_Data;
SRB[17]:=LSeg_Data;
SRB[18]:=HSeg_Data;
SRB[23]:=6;
SRB[64]:=$15;
SRB[65]:=$10;
SRB[68]:=12;
FillChar (DataBuffer^[0],12,0);
DataBuffer^[2]:=$10; { Buffered mode }
DataBuffer^[3]:=8;
DataBuffer^[10]:=Hi(BufferSize);
DataBuffer^[11]:=Lo(BufferSize);
{ Execute }
CallASPI;
End;
{ Write the block in 'Buffer' to tape. }
{ }
Procedure ASPITape.WriteData (Buffer : Pointer; BufferSize : Word);
Begin
{ ASPI Setup for Block Write }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=Lo(BufferSize);
SRB[11]:=Hi(BufferSize);
SRB[15]:=Lo(Ofs(Buffer^));
SRB[16]:=Hi(Ofs(Buffer^));
SRB[17]:=Lo(Seg(Buffer^));
SRB[18]:=Hi(Seg(Buffer^));
SRB[23]:=6;
SRB[64]:=$0A;
SRB[65]:=$01;
SRB[68]:=1; { Write one block of size set in MODE SELECT }
{ Execute }
CallASPI;
{ Reset TarBlock count }
TarBlock := 0;
End;
{ Reads the next block of 'BufferSize' and stores it in 'Buffer'. }
{ }
Procedure ASPITape.ReadData(Buffer : Pointer; BufferSize : Word);
Begin
{ ASPI Setup for Block Read }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=Lo(BufferSize);
SRB[11]:=Hi(BufferSize);
SRB[15]:=Lo(Ofs(Buffer^));
SRB[16]:=Hi(Ofs(Buffer^));
SRB[17]:=Lo(Seg(Buffer^));
SRB[18]:=Hi(Seg(Buffer^));
SRB[23]:=6;
SRB[64]:=$08;
SRB[65]:=$01;
SRB[68]:=1; { Read one block of size set in MODE SELECT }
{ Execute }
CallASPI;
End;
{ Returns tape position in terms of logical blocks. }
{ }
Function ASPITape.TapePosition : Longint;
Type
B4 = Array [0..4] Of Byte;
Var
L : Longint;
LA : B4 Absolute L;
Begin
{ ASPI Setup for Position Inquiry }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[10]:=20;
SRB[15]:=LOfs_Data;
SRB[16]:=HOfs_Data;
SRB[17]:=LSeg_Data;
SRB[18]:=HSeg_Data;
SRB[23]:=10;
SRB[64]:=$34;
{ Execute }
CallASPI;
{ Extract Information }
If ((DataBuffer^[0] And 4)<>0) Then
TapePosition := -1 { Position unknown }
Else Begin
LA[0] := DataBuffer^[7];
LA[1] := DataBuffer^[6];
LA[2] := DataBuffer^[5];
LA[3] := DataBuffer^[4];
TapePosition := L;
End;
End;
{ Locate to a specific logical block. Any direction works. }
{ }
Procedure ASPITape.LocateTape (Location : Longint);
Begin
{ ASPI Setup for Set Position }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=10;
SRB[64]:=$2B;
SRB[67]:= ((Location SHR 24) AND $8F);
SRB[68]:= ((Location SHR 16) AND $FF);
SRB[69]:= ((Location SHR 8) AND $FF);
SRB[70]:= (Location And $FF);
{ Execute }
CallASPI;
End;
{ Resets ASPI and the device connected to it. }
{ Causes a rewind. }
{ }
Procedure ASPITape.ASPIReset;
Begin
{ ASPI Setup for Reset }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=4;
SRB[ 8]:=Info.Target;
{ Execute }
CallASPI;
End;
{ Write 'Number' filemarks to the tape }
{ }
Procedure ASPITape.WriteFilemark (Number : Byte);
Begin
{ ASPI Setup for Write Filemark }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$10;
SRB[68]:=Number;
{ Execute }
CallASPI;
End;
{ Space over 'Spacing' filemarks }
{ }
Procedure ASPITape.SpaceFilemark (Spacing : Longint);
Begin
{ ASPI Setup for Space Filemark }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$11;
SRB[65]:=1;
SRB[66]:=((Spacing SHR 16) AND $FF);
SRB[67]:=((Spacing SHR 8) AND $FF);
SRB[68]:=(Spacing AND $FF);
{ Execute }
CallASPI;
End;
{ Set the tape to the 'End of Data'-position }
{ }
Procedure ASPITape.GotoEnd;
Begin
{ ASPI Setup for Spacing to EOD }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$11;
SRB[65]:=3;
{ Execute }
CallASPI;
End;
{ Rewinds the tape to the begining. }
{ }
Procedure ASPITape.Rewind;
Begin
{ ASPI Setup for Rewind }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$01;
{ Execute }
CallASPI;
End;
Procedure ASPITape.Unload;
Begin
{ ASPI Setup for Unload }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$1B;
SRB[68]:=0;
{ Execute }
CallASPI;
End;
Procedure ASPITape.Erase;
Begin
{ ASPI Setup for Erase }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$19;
SRB[65]:=1;
{ Execute }
CallASPI;
End;
Procedure ASPITape.Load;
Begin
{ ASPI Setup for Load }
FillChar (SRB,SizeOf(SRB),0);
SRB[ 0]:=2;
SRB[ 8]:=Info.Target;
SRB[23]:=6;
SRB[64]:=$1B;
SRB[68]:=1;
{ Execute }
CallASPI;
End;
{ ============================================================ }
{ Initialisation code }
Begin
LockSRB := False;
End.